package com.powernode.listener;

import com.powernode.constant.Constants;
import com.powernode.entity.Orders;
import com.powernode.service.DelayMessageService;
import com.powernode.service.OrderService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.Channel;
import jakarta.annotation.Resource;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * 订单消息监听器组件
 * 负责从RabbitMQ队列接收订单创建消息，处理订单入库、结果缓存及延迟消息发送
 * 解决高并发场景下的订单异步处理问题
 */
@Component
public class OrderListener {
    @Resource
    private ObjectMapper objectMapper; // JSON序列化工具，用于消息体与对象转换

    @Resource
    private OrderService orderService; // 订单业务逻辑层，处理订单创建核心逻辑

    @Resource
    private RedisTemplate redisTemplate; // Redis操作模板，用于缓存订单结果

    @Resource
    private DelayMessageService delayMessageService; // 延迟消息服务，用于发送订单超时检查消息

    /**
     * 消息消费方法 - 处理订单创建请求
     * @RabbitListener: 声明监听的队列名称为Constants.QUEUE_NAME_ORDER，并发消费者数量为6
     * 并发数配置说明：IO密集型任务建议设置为CPU核心数+1，此处6为经验值
     * @param message RabbitMQ消息对象，包含消息体和属性
     * @param channel 消息通道，用于手动确认消息
     */
    @RabbitListener(queues = Constants.QUEUE_NAME_ORDER, concurrency = "6")
    public void onMessage(Message message, Channel channel)  {
        // 获取消息投递标签（唯一标识），用于消息确认
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            // 1. 消息体转换：将JSON格式的消息体转换为Orders对象
            Orders orders = objectMapper.readValue(message.getBody(), Orders.class);

            // ====== 新增：重复订单检查 ======
            // 1. 先查Redis缓存（高性能）
            String orderKey = Constants.REDIS_RESULT + orders.getGoodsId() + ":" + orders.getUid();
            Object cachedOrder = redisTemplate.opsForValue().get(orderKey);
            if (cachedOrder != null) {
                channel.basicAck(deliveryTag, false);
                return;
            }

            // 2. 再查数据库（最终一致性）
            Orders existingOrder = orderService.queryOrderByUidAndGoodsId(orders.getUid(), orders.getGoodsId());
            if (existingOrder != null) {
                redisTemplate.opsForValue().set(orderKey, existingOrder);
                channel.basicAck(deliveryTag, false);
                return;
            }
            // ====== 新增：重复订单检查 ======

            // 2. 订单入库：调用订单服务执行订单创建逻辑
            int result = orderService.doOrder(orders);

            if (result == 1) { // 订单创建成功
                // 3. 结果缓存：将订单信息存入Redis，用于前端查询秒杀结果
                // 缓存键格式：Constants.REDIS_RESULT + 商品ID + 冒号 + 用户ID
                redisTemplate.opsForValue().set(Constants.REDIS_RESULT + orders.getGoodsId() + ":" + orders.getUid(), orders);

                // 4. 手动确认消息：使用channel.basicAck确认消息消费成功
                // 参数说明：deliveryTag-消息标签，false-不批量确认
                channel.basicAck(deliveryTag, false);

                // 5. 发送延迟消息：调用延迟消息服务，为当前订单发送超时检查消息
                // 用于处理用户下单后未支付的场景（如15分钟未支付自动取消）
                delayMessageService.sendMessage(orders.getId());
                return; // 处理完成，退出方法
            }

            // 订单创建失败 - 消息拒绝策略
            // basicNack参数说明：deliveryTag-消息标签，false-不批量拒绝，true-将消息重新入队
            channel.basicNack(deliveryTag, false, true);
        } catch (Exception e) { // 异常处理逻辑
            // 发生异常时拒绝消息并重新入队
            try {
                channel.basicNack(deliveryTag, false, true);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
            throw new RuntimeException(e); // 抛出运行时异常，由Spring AMQP捕获处理
        }
    }
}
